﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Terminal.Advanced_AEUC_settings_controls.Settings_AUX;

namespace Terminal.Advanced_AEUC_settings_controls.Scripting
{
    /// <summary>
    /// Delegate for the script print fired event
    /// </summary>
    /// <param name="text">Text to print</param>
    public delegate void ScriptPrint(string text);

    /// <summary>
    /// Script execution module
    /// </summary>
    class ScrMainModule
    {
        /// <summary>
        /// OMX Component handle struct
        /// </summary>
        public class OMXCompScrHdlr
        {
            /// <summary>
            /// OMX component name
            /// </summary>
            public string OMXCompName;
            /// <summary>
            /// All of the script registered payload structs (including system variables)
            /// </summary>
            public GenPayloadStruct[] scrPldStructs;
        }
        /// <summary>
        /// Scripting components
        /// </summary>
        public class ScrComp
        {
            /// <summary>
            /// Possible script actions and commands
            /// </summary>
            public ScrBasicAction[] scrActions;
            /// <summary>
            /// All of the script registered messages
            /// </summary>
            public GenMSG[] scrMessages;
            /// <summary>
            /// All of the script registered OMX components (including system variables)
            /// </summary>
            public OMXCompScrHdlr[] compHdls;

            public ScrRunTimeEnv rteCmn;

            /// <summary>
            /// Public constructor
            /// </summary>
            /// <param name="iScrActions">Script actions and commands</param>
            /// <param name="iScrMessages">Script registered messages</param>
            /// <param name="iScrPldStructs">Script registered payload structs</param>
            public ScrComp(ScrBasicAction[] iScrActions, GenMSG[] iScrMessages, OMXCompScrHdlr[] iCompHdls)
            {
                scrActions = iScrActions;
                scrMessages = iScrMessages;
                compHdls = iCompHdls;
                rteCmn = new ScrRunTimeEnv();
            }
        }

        /// <summary>
        /// Associated messages, payload structs and actions for that script instace
        /// </summary>
        public ScrComp components;
        /// <summary>
        /// Delegate for script component payload struct updating
        /// </summary>
        /// <param name="newPldStr">New payload struct to add</param>
        /// <returns>Opperation success</returns>
        public delegate bool UpdateScrComponents(GenPayloadStruct newPldStr);
        /// <summary>
        /// Delegate for the script fired events
        /// </summary>
        /// <param name="ErrorStatus">Status of the script module</param>
        public delegate void ScriptEvent(string ErrorStatus);
        /// <summary>
        /// Delegate for the script execution function
        /// </summary>
        public delegate void ScriptExec();
        /// <summary>
        /// Delegate for setting script runtime function
        /// </summary>
        /// <param name="ascRte">New script runtime</param>
        public delegate void ScriptSrte(ScrRunTimeEnv ascRte);
        /// <summary>
        /// Creates a progress bar on the script main view
        /// </summary>
        /// <param name="name">Display name for the progress bar</param>
        /// <param name="length">Units length of the progress bar</param>
        public delegate ProgressBars.ProgBarsHdl ScriptSetUpProgBar(string name, int length);
        /// <summary>
        /// Perform a step on the script progress bar
        /// </summary>
        public delegate void ScriptStepProgBar(ProgressBars.ProgBarsHdl progHdl);
        /// <summary>
        /// Hide the script progress bar
        /// </summary>
        public delegate void ScriptRemoveProgBar(ProgressBars.ProgBarsHdl progHdl);
        /// <summary>
        /// Script messaging center to use for this scrip instance
        /// </summary>
        ScrMsgCntr messaging;
        /// <summary>
        /// Possible conditional execution operations (id).
        /// </summary>
        const int COND_ELSE = 0, COND_ELIF = 1, COND_ENDIF = 2, COND_IF = 3, COND_STRCOMP = 4;
        /// <summary>
        /// Array of the display text of the possible conditional execution operations
        /// </summary>
        string[] condOps;
        /// <summary>
        /// Runtime error statu string, for information and execution reports.
        /// </summary>
        string errorStatus;
        /// <summary>
        /// Semaphore to block upon when a line break point has been reached. Released by UI event (or terminate event).
        /// </summary>
        Semaphore lnBreakPoint;

        /// <summary>
        /// Public constructor
        /// </summary>
        /// <param name="commObj">CommLink object to use ase messaging media</param>
        /// <param name="iScrMessages">Defined messages array that will be available to the script</param>
        /// <param name="iScrPldStructs">Defined struct payloads array that will be available to the script</param>
        public ScrMainModule(CommLink.CommLink commObj, GenMSG[] iScrMessages, OMXCompScrHdlr[] iScrCompStructs, ScriptExec crtDefCnct)
        {
            messaging = new ScrMsgCntr(commObj);

            //TODO: Serialize/Deserialize
            int itr = 0;
            ScrBasicAction[] scrActions = new ScrBasicAction[25];

            scrActions[itr++] = new ScrActSendMsg("SEND", "Sends a message", messaging.SendMessage);
            scrActions[itr++] = new ScrActWaitMsg("RECEIVE", "Receives a message (waits for a message to be received or processes a received message", messaging);
            scrActions[itr++] = new ScrActSleep("SLEEP", "Suspends script execution for an amount of time");

            condOps = new string[]{ "ELSE", "ELIF", "ENDIF", "IF", "STR_CMP" };

            scrActions[itr++] = new ScrCondIf(condOps[COND_IF], "Conditional execution - \"if\" block");
            scrActions[itr++] = new ScrCondElif(condOps[COND_ELIF], "Conditional execution - \"else if\" block");
            scrActions[itr++] = new ScrCondElse(condOps[COND_ELSE], "Conditional execution - \"else\" block");
            scrActions[itr++] = new ScrCondEndif(condOps[COND_ENDIF], "Conditional execution - closes the last block");
            scrActions[itr++] = new ScrStrCmp(condOps[COND_STRCOMP], "Conditional execution - \"if\" block - string compare");
            scrActions[itr++] = new ScrActGoTo("GOTO", "Jump to label");
            
            scrActions[itr++] = new ScrActCpyStrct("STRUCT_CPY", "Copies first struct to seconde struct");
            scrActions[itr++] = new ScrActCreateStrct("STRUCT_CRT", "Creates a new component payload struct");
            scrActions[itr++] = new ScrActStrctMarshalling("STRUCT_MSHL", "Allocates memory on target, serializes a variable count of structures and transfers them to target", messaging.SendMessage, messaging);
            scrActions[itr++] = new ScrActFileMarshalling("FILE_MSHL", "Copies a file to allocated memory on target", messaging.SendMessage, messaging);
            scrActions[itr++] = new ScrActPrepDCCFiles("DCC_FILE_PREP", "Prepares a file containing all of the specified sensor settings");

            scrActions[itr++] = new ScrPrint("PRINT", "Prints objects or texts. Integer literals should be ';'-postfixed.\n Ex. PRINT \"Print string\" 65536; Cam_cap_port $.SomeGenericValue");
            scrActions[itr++] = new ScrInvokeScriptFile("INVOKE_SCRIPT", "Executes a script file");

            scrActions[itr++] = new ScrCrtDefCnction("CRT_DEF_CNCT", "Create default communication media", crtDefCnct);
            scrActions[itr++] = new ScrConnect("CNCT", "Connect or disconnect communication media", messaging);
            scrActions[itr++] = new ScrPwrCtrl("PWR_CTRL", "Power control");

            scrActions[itr++] = new ScrChkStatus("GET_LAST_STATUS", "Get last message status");
            scrActions[itr++] = new ScrStopOnErrorStatus("STOP_EXEC_ON_ERROR", "Stop or continue script execution on erroneous message status");

            scrActions[itr++] = new ScrConcatString("STRING_ADD", "Concatenates two strings");

            scrActions[itr++] = new ScrStrctSz("STRUCT_SIZEOF", "Returns the size in bytes of a structure");
            scrActions[itr++] = new ScrActStrctMemcpy("STRUCT_MEMCPY", "Copies a file to allocated memory on target", messaging.SendMessage, messaging);
            scrActions[itr++] = new StrActStrctMemcpyFrom("STRUCT_MEMREAD", "Copies a file from allocated memory on target", messaging.SendMessage, messaging);
            
            int pldStructsCnt = 0;
            if (iScrCompStructs != null)
                pldStructsCnt += iScrCompStructs.Length;

            OMXCompScrHdlr[] scrPldStructs = new OMXCompScrHdlr[pldStructsCnt + 1];
            iScrCompStructs.CopyTo(scrPldStructs, 1);

            scrPldStructs[0] = new OMXCompScrHdlr();
            scrPldStructs[0].OMXCompName = "RTE";
            scrPldStructs[0].scrPldStructs = new GenPayloadStruct[1];
            scrPldStructs[0].scrPldStructs[0] = new GenPayloadStruct("$", null);
            scrPldStructs[0].scrPldStructs[0].Editable = true;
            //END TODO

            components = new ScrComp(scrActions, iScrMessages, scrPldStructs);
            components.rteCmn.updateComp = new UpdateScrComponents(UpdatePayloadCompnts);
            components.rteCmn.Print = new ScriptPrint(commObj.errorMessage);
            components.rteCmn.scrExec = new ScriptExec(execScript);
            components.rteCmn.scrSourcesify = new ScriptExec(sourcesifyScript);
            components.rteCmn.scrSetRte = new ScriptSrte(setNewRTE);
        }

        /// <summary>
        /// Stops the current scripting main module
        /// </summary>
        public void ScrMainModuleStop()
        {
            components.rteCmn.FireScrTerminating();
            messaging.ScrMsgCntrStop();
        }
        
        /// <summary>
        /// Destructor
        /// </summary>
        ~ScrMainModule()
        {
            ScrMainModuleStop();
        }
        
        /// <summary>
        /// Checks if there is anything that can be interpreted on the specified line to put a break point on and advances to the first eligible line
        /// </summary>
        /// <param name="cmdLines">Command lines array that is being examined</param>
        /// <param name="insertIdx">Index at which to insert the breakpoint</param>
        /// <returns>Actual eligible index where the breakpoint can be inserted</returns>
        int CheckLineValidity(string[] cmdLines, int insertIdx)
        {
            if (cmdLines != null)
            {
                for (int i = insertIdx; i < cmdLines.Length; i++)
                {
                    if (ScrCmdParser.ExecLineValidity(cmdLines[i]))
                        return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// Adds a new payload structure to the script payload components
        /// </summary>
        /// <param name="newPldStr">New payload structure</param>
        /// <returns>Opperation success</returns>
        bool UpdatePayloadCompnts(GenPayloadStruct newPldStr)
        {
            //TODO: add system struct reference
            int iCom = 0;
            if (newPldStr != null)
            {
                for (int i = 0; i < components.compHdls[iCom].scrPldStructs.Length; i++)
                {
                    if (components.compHdls[iCom].scrPldStructs[i].dispName == newPldStr.dispName)
                    {
                        if (components.compHdls[iCom].scrPldStructs[i].Editable)
                        {
                            components.compHdls[iCom].scrPldStructs[i] = newPldStr;
                            return true;
                        }
                        else
                            return false;
                    }
                }
                GenPayloadStruct[] updated = new GenPayloadStruct[components.compHdls[iCom].scrPldStructs.Length + 1];
                components.compHdls[iCom].scrPldStructs.CopyTo(updated, 0);
                updated[components.compHdls[iCom].scrPldStructs.Length] = newPldStr;
                components.compHdls[iCom].scrPldStructs = updated;
            }

            components.rteCmn.FireCompUpdate(errorStatus);

            return true;
        }

        /// <summary>
        /// Register function for printing messages
        /// </summary>
        /// <param name="printF">Message print function</param>
        public void RegisterPrintFunction(ScriptPrint printF)
        {
            components.rteCmn.Print = printF;
        }

        /// <summary>
        /// Adds a breakpoint to input line number
        /// </summary>
        /// <param name="posInsrt">Input line number</param>
        public void AddBreakPoint(int posInsrt)
        {
            int validPos = CheckLineValidity(components.rteCmn.cmdLns, posInsrt);

            if (validPos == -1)
                return;

            if (components.rteCmn.breakPoints == null)
            {
                components.rteCmn.breakPoints = new List<int>();
                components.rteCmn.breakPoints.Add(validPos);
                return;
            }
            //Adds the breakpoint sorted, so later runtime searching is optimized
            for (int i = 0; i < components.rteCmn.breakPoints.Count; i++)
            {
                if (components.rteCmn.breakPoints[i] > validPos)
                {
                    components.rteCmn.breakPoints.Insert(i, validPos);
                    return;
                }
            }

            components.rteCmn.breakPoints.Add(validPos);
        }
        /// <summary>
        /// Removes a specified line number breakpoint from the active breakpoints.
        /// </summary>
        /// <param name="posRem">Line number breakpoint</param>
        public void RemBreakPoint(int posRem)
        {
            if (components.rteCmn.breakPoints != null)
            {
                for (int i = 0; i < components.rteCmn.breakPoints.Count; i++)
                {
                    if (components.rteCmn.breakPoints[i] == posRem)
                    {
                        components.rteCmn.breakPoints.RemoveAt(i);
                        break;
                    }
                }

                if (components.rteCmn.breakPoints.Count == 0)
                    components.rteCmn.breakPoints = null;
            }
        }
        /// <summary>
        /// Checks if the input a breakpoint is set on the input line number.
        /// </summary>
        /// <param name="posCheck">Input line number</param>
        /// <returns>Bollean result</returns>
        public bool CheckBreakPointHit(int posCheck)
        {
            if (components.rteCmn.breakPoints == null)
                return false;

            for (int i = 0; i < components.rteCmn.breakPoints.Count; i++)
            {
                if (components.rteCmn.breakPoints[i] == posCheck)
                    return true;
                else if (components.rteCmn.breakPoints[i] > posCheck)
                    return false;   //Since the breakPoint list is ascending ordered, 
                                    //if the item is greater than the searchee, there 
                                    //is no further chance to find it.
            }
            return false;
        }
        /// <summary>
        /// Returns an array of the current set breakpoints.
        /// </summary>
        /// <returns>Integer array of the breakpoints</returns>
        public int[] GetBreakPoints()
        {
            if (components.rteCmn.breakPoints != null)
                return components.rteCmn.breakPoints.ToArray();
            else
                return null;
        }

        /// <summary>
        /// Sets the command execution counter to the specified command line
        /// </summary>
        /// <param name="lnPos">Command line to set as next valid line</param>
        public void SetExecToLine(int lnPos)
        {
            int validPos = CheckLineValidity(components.rteCmn.cmdLns, lnPos);

            if (validPos == -1)
                return;

            components.rteCmn.crntCmdLine = validPos;

            userChangedCmdLine = true;

            components.rteCmn.FireExecLineChange(errorStatus);
        }
        /// <summary>
        /// Runs script command execution to the specified command line
        /// </summary>
        /// <param name="lnPos">Halt script execution once this line is reached</param>
        public void RunExecToLine(int lnPos)
        {
            int validPos = CheckLineValidity(components.rteCmn.cmdLns, lnPos);

            if (validPos == -1 || validPos <= components.rteCmn.crntCmdLine)
                return;

            components.rteCmn.trgtCmdLine = validPos;
            components.rteCmn.runningMode = ScrRunTimeEnv.RunMode.FREE_RUN;

            resumeScrExec();
        }
        /// <summary>
        /// Returns the number of the command line that is currently being executed or -1 script is not running
        /// </summary>
        /// <returns>Current line number</returns>
        public int GetCrntExecLine()
        {
            return components.rteCmn.crntCmdLine;
        }

        /// <summary>
        /// Sets the active command lines that are to be executed
        /// </summary>
        /// <param name="nCmdLines">Array of the new command lines</param>
        public void SetActiveCmdLines(string[] nCmdLines)
        {
            lock (this)
            {
                components.rteCmn.cmdLns = nCmdLines;
            }
        }
        /// <summary>
        /// Gets the currently executed command lines
        /// </summary>
        /// <returns>Command line string collection</returns>
        public string[] GetActiveCmdLines()
        {
            return components.rteCmn.cmdLns;
        }
        /// <summary>
        /// Returns the bytestream of a selected structure
        /// </summary>
        /// <param name="compName">Component containing structure</param>
        /// <param name="structName">Structure name</param>
        /// <returns>Structure bytestream</returns>
        public byte[] GetStruct(string compName, string structName)
        {
            GenPayloadStruct stSel = ScrCmdParser.ResolveScriptPayloadStruct(compName, structName, components);

            return stSel.preparePureByteStream();
        }
        /// <summary>
        /// Returns the bytestream of a selected structure's field
        /// </summary>
        /// <param name="compName">Component containing structure</param>
        /// <param name="structName">Structure name</param>
        /// <param name="fieldName">Field name</param>
        /// <returns>Structure bytestream</returns>
        public byte[] GetStructField(string compName, string structName, string fieldName)
        {
            GenPayloadStruct stSel = ScrCmdParser.ResolveScriptPayloadStruct(compName, structName, components);

            byte[] ret = new byte[0];

            Object val = stSel[stSel[fieldName]];
            if (val.GetType() == typeof(Byte))
            {
                ret = new byte[1];
                ret[0] = (Byte)val;
            }
            else if (val.GetType() == typeof(UInt16))
            {
                UInt16 ui16V = (UInt16)val;
                
                ret = new byte[2];
                ret[0] = (Byte)ui16V;
                ret[1] = (Byte)(ui16V >> 8);
            }
            else if (val.GetType() == typeof(UInt32))
            {
                UInt32 ui32V = (UInt32)val;

                ret = new byte[4];
                ret[0] = (Byte)ui32V;
                ret[1] = (Byte)(ui32V >> 8);
                ret[2] = (Byte)(ui32V >> 16);
                ret[3] = (Byte)(ui32V >> 24);
            }
            else if (val.GetType() == typeof(Int16))
            {
                Int16 ui16V = (Int16)val;

                ret = new byte[2];
                ret[0] = (Byte)ui16V;
                ret[1] = (Byte)(ui16V >> 8);
            }
            else if (val.GetType() == typeof(Int32))
            {
                Int32 ui32V = (Int32)val;

                ret = new byte[4];
                ret[0] = (Byte)ui32V;
                ret[1] = (Byte)(ui32V >> 8);
                ret[2] = (Byte)(ui32V >> 16);
                ret[3] = (Byte)(ui32V >> 24);
            }
            else if (val.GetType() == typeof(Byte[]))
            {
                ret = (byte[])val;
            }

            return ret;
        }

        int execWatchDog = 0;
        int crntScriptLine = 0;
        bool userChangedCmdLine = false;

        /// <summary>
        /// Executes an array of command lines
        /// </summary>
        /// <param name="commandLines">Command lines to execute</param>
        /// /// <param name="initRunMode">Initial run mode - either free run or stepped</param>
        /// <returns>Success status of the command execution</returns>
        public bool Execute(string[] commandLines, ScrRunTimeEnv.RunMode initRunMode, string scriptName, bool threaded, int cmdLineStart)
        {
            execWatchDog = 0;
            components.rteCmn.crntCmdLine = cmdLineStart;
            components.rteCmn.trgtCmdLine = -1;
            components.rteCmn.cmdLns = commandLines;
            components.rteCmn.SilentRun = false;
            components.rteCmn.StopOnErrorMsgStatus = true;
            components.rteCmn.c_source = scriptName;
            lnBreakPoint = new Semaphore(0, 1);

            components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.NORMAL;
            components.rteCmn.runningMode = initRunMode;
            messaging.ResetMessageDispatch();

            components.rteCmn.FireScrDone("Executing: " + components.rteCmn.c_source);

            if (threaded)
            {
                Thread scriptExec = new Thread(new ThreadStart(execScript));
                scriptExec.IsBackground = true;
                scriptExec.Start();
            }
            else
            {
                execScript();
            }

            return true;
        }

        /// <summary>
        /// Terminates the current script execution if any
        /// </summary>
        public void TerminateExecution()
        {
            components.rteCmn.crntCmdLine = components.rteCmn.cmdLns.Length - 1;

            userChangedCmdLine = true;

            components.rteCmn.FireExecLineChange(errorStatus);
        }

        /// <summary>
        /// Sourcesify an array of command lines
        /// </summary>
        /// <param name="modName">Generated C module entry name</param>
        /// <param name="commandLines">Command lines to execute</param>
        /// <param name="initRunMode">Initial run mode - either free run or stepped</param>
        /// <returns>Success status of the command execution</returns>
        public bool Sourcesify(string modName, string[] commandLines, ScrRunTimeEnv.RunMode initRunMode)
        {
            execWatchDog = 0;
            components.rteCmn.crntCmdLine = 0;
            components.rteCmn.trgtCmdLine = -1;
            components.rteCmn.cmdLns = commandLines;
            components.rteCmn.SilentRun = false;
            components.rteCmn.StopOnErrorMsgStatus = true;
            components.rteCmn.c_source = modName;
            lnBreakPoint = new Semaphore(0, 1);

            components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.NORMAL;
            components.rteCmn.runningMode = initRunMode;
            messaging.ResetMessageDispatch();

            components.rteCmn.FireScrDone("Source-generating " + modName);

            Thread scriptSourcesify = new Thread(new ThreadStart(sourcesifyScript));
            scriptSourcesify.IsBackground = true;
            scriptSourcesify.Start();

            return true;
        }

        /// <summary>
        /// Sets new script runtime
        /// </summary>
        /// <param name="nScrRte">The new script runtime to apply</param>
        void setNewRTE(ScrRunTimeEnv nScrRte)
        {
            components.rteCmn = nScrRte;
        }

        /// <summary>
        /// Sets the script state to running and releases pending break point
        /// </summary>
        public void SetScriptRun()
        {
            components.rteCmn.runningMode = ScrRunTimeEnv.RunMode.FREE_RUN;
            resumeScrExec();
        }
        /// <summary>
        /// Sets the script state to running and releases pending break point
        /// </summary>
        public void SetScriptStep()
        {
            components.rteCmn.runningMode = ScrRunTimeEnv.RunMode.STEP_RUN;
            resumeScrExec();
        }

        /// <summary>
        /// Fires the halt sxript event and suspends script execution. 
        /// </summary>
        void haltScrExec()
        {
            components.rteCmn.FireBreakReached(errorStatus);
            lnBreakPoint.WaitOne();
        }
        /// <summary>
        /// Resumes script execution
        /// </summary>
        void resumeScrExec()
        {
            try
            {
                lnBreakPoint.Release();
            }
            catch
            {
            }
        }
        /// <summary>
        /// Fires the script execution done event with the updated runtime error status
        /// </summary>
        void scrExecDone()
        {
            execWatchDog--;
            components.rteCmn.crntCmdLine = -1;
            components.rteCmn.FireScrDone(errorStatus);
        }
        /// <summary>
        /// Fires the script execution done event with the updated runtime error status
        /// </summary>
        void scrSourcesificationDone()
        {
            execWatchDog--;
            components.rteCmn.crntCmdLine = -1;
            if (execWatchDog == 0)
            {
                components.rteCmn.FireScrSourceGenDone(errorStatus);
            }
        }
        /// <summary>
        /// Checks if it is needed to break at this command and fires events related to new command execution
        /// </summary>
        /// <returns>True if user change of command line has occured, to reposition and rescan active command line</returns>
        bool NewCmdCommence()
        {
            components.rteCmn.FireExecLineChange(errorStatus);

            if (components.rteCmn.runningMode == ScrRunTimeEnv.RunMode.STEP_RUN || crntScriptLine == components.rteCmn.trgtCmdLine || CheckBreakPointHit(components.rteCmn.crntCmdLine))
            {
                if (crntScriptLine == components.rteCmn.trgtCmdLine)
                    components.rteCmn.trgtCmdLine = -1;
                haltScrExec();
            }

            if (components.rteCmn.InvokeEnded)
            {
                lock (components.rteCmn)
                {
                    components.rteCmn.InvokeEnded = false;
                    crntScriptLine = components.rteCmn.crntCmdLine;
                }
            }

            if (userChangedCmdLine)
            {
                userChangedCmdLine = false;
                lock (components.rteCmn)
                {
                    crntScriptLine = components.rteCmn.crntCmdLine;
                }
                return true;
            }

            return false;
        }


        /// <summary>
        /// Script execution thread
        /// </summary>
        void execScript()
        {
            if (execWatchDog++ == 100)
                throw new Exception("Too many script invokations - terminating current thread!");
                
            int linePos = 0;
            int condFound;
            bool executionOK = true;
            string thrownErrors = null;

            components.rteCmn.ScrRecId = execWatchDog;

            lock (components.rteCmn)
            {
                crntScriptLine = components.rteCmn.crntCmdLine;
            }

            for (; crntScriptLine < components.rteCmn.cmdLns.Length && executionOK; )
            {
                if (ScrCmdParser.ExecLineValidity(components.rteCmn.cmdLns[crntScriptLine]))
                {
                    try
                    {
                        linePos = 0;

                        while (linePos < components.rteCmn.cmdLns[crntScriptLine].Length && executionOK)
                        {
                            if (components.rteCmn.condExecution == ScrRunTimeEnv.CondExecMode.NORMAL)
                            {
                                if (NewCmdCommence())
                                    break;
                                executionOK = ScrCmdParser.ExecCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, components);

                                //Chek if this call is returning from sub script execution and break from
                                //the while loop, so that the crntScriptLine variable can be restored to
                                //the value prior to invoking
                                if (components.rteCmn.InvokeEnded)
                                    break;
                            }
                            else if (components.rteCmn.condExecution == ScrRunTimeEnv.CondExecMode.SKIP_IF)
                            {
                                #region Execute skip IF block handling
                                //Check if this line has any condotional commands embeded
                                if (ScrCmdParser.SearchCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, condOps, out condFound))
                                {
                                    if (NewCmdCommence())
                                        break;
                                    //If ELIF command is found, rewind the linePos so the ExecCmdLine will process the
                                    //conditional command also.
                                    if (condFound == COND_ELIF)
                                    {
                                        linePos -= condOps[condFound].Length;
                                        executionOK = ScrCmdParser.ExecCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, components);
                                    }
                                    else//If ELSE or ENDIF command is found, the command can be skipped as it is void and
                                    {   //after the line is processed, script conditional execution mode can be returned to NORMAL
                                        executionOK = ScrCmdParser.ExecCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, components);
                                        components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.NORMAL;
                                    }

                                    //Chek if this call is returning from sub script execution and break from
                                    //the while loop, so that the crntScriptLine variable can be restored to
                                    //the value prior to invoking
                                    if (components.rteCmn.InvokeEnded)
                                        break;
                                }
                                #endregion
                            }
                            else if (components.rteCmn.condExecution == ScrRunTimeEnv.CondExecMode.EXEC_IF)
                            {
                                #region Execute IF block handling
                                int preSrchLinePos = linePos;
                                string truncedCmdLine;
                                //Check if there are no other conditional commands on the line
                                if (!ScrCmdParser.SearchCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, condOps, out condFound))
                                    //If there are no other conditional commands, execute the entire line.
                                    truncedCmdLine = components.rteCmn.cmdLns[crntScriptLine];
                                else
                                    //If any conditional commands are found, execute the line up to those commands only.
                                    truncedCmdLine = components.rteCmn.cmdLns[crntScriptLine].Substring(0, linePos - condOps[condFound].Length);

                                if (NewCmdCommence())
                                    break;
                                linePos = preSrchLinePos;
                                executionOK = ScrCmdParser.ExecCmdLine(truncedCmdLine, ref linePos, components);

                                //Depending if the found command was ELSE or ELIT
                                if (condFound == COND_ELSE || condFound == COND_ELIF)
                                    //Set the script conditional execution mode to GOTO ENDIF
                                    components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.GOTO_ENDIF;
                                else if (condFound == COND_ENDIF)
                                    // If ENDIF command has been encountered, return script conditional execution mode to NORMAL
                                    components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.NORMAL;

                                //Chek if this call is returning from sub script execution and break from
                                //the while loop, so that the crntScriptLine variable can be restored to
                                //the value prior to invoking
                                if (components.rteCmn.InvokeEnded)
                                    break;
                                #endregion
                            }
                            else if (components.rteCmn.condExecution == ScrRunTimeEnv.CondExecMode.GOTO_ENDIF)
                            {
                                #region Go to ENDIF handling
                                string[] endif = new string[1];
                                endif[0] = condOps[COND_ENDIF];
                                //Execute command line only if ENDIF command has been found in it, and only the commands after the ENDIF
                                if (ScrCmdParser.SearchCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, endif, out condFound))
                                {
                                    if (NewCmdCommence())
                                        break;
                                    executionOK = ScrCmdParser.ExecCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, components);
                                    //Once the command line has been processed, return script conditional execution mode to NORMAL
                                    components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.NORMAL;

                                    //Chek if this call is returning from sub script execution and break from
                                    //the while loop, so that the crntScriptLine variable can be restored to
                                    //the value prior to invoking
                                    if (components.rteCmn.InvokeEnded)
                                        break;
                                }
                                #endregion
                            }

                            if (components.rteCmn.condExecution == ScrRunTimeEnv.CondExecMode.GOTO_LABEL)
                            {
                                if (components.rteCmn.rteResult != null && components.rteCmn.rteResult.GetType() == typeof(string))
                                {
                                    #region GOTO handling
                                    string[] destLabel = new string[1];
                                    destLabel[0] = (string)components.rteCmn.rteResult + ":";
                                    //set the run time status to success
                                    components.rteCmn.rteResult = (uint)0;
                                    bool lblDone = false;
                                    //Reset the current command line at the beginning of the script commands array
                                    for (crntScriptLine = 0; crntScriptLine < components.rteCmn.cmdLns.Length && !lblDone; crntScriptLine++)
                                    {
                                        //Reset the line position to the beginning of a line
                                        linePos = 0;
                                        while (linePos < components.rteCmn.cmdLns[crntScriptLine].Length && !lblDone)
                                        {
                                            //Execute command line only if label has been found in it, and only the commands after the label
                                            if (ScrCmdParser.SearchCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, destLabel, out condFound))
                                            {
                                                if (NewCmdCommence())
                                                    break;
                                                executionOK = ScrCmdParser.ExecCmdLine(components.rteCmn.cmdLns[crntScriptLine], ref linePos, components);
                                                //Once the command line has been processed, return script conditional execution mode to NORMAL
                                                components.rteCmn.condExecution = ScrRunTimeEnv.CondExecMode.NORMAL;
                                                lblDone = true;

                                                //Chek if this call is returning from sub script execution and break from
                                                //the while loop, so that the crntScriptLine variable can be restored to
                                                //the value prior to invoking
                                                if (components.rteCmn.InvokeEnded)
                                                    break;
                                            }
                                        }
                                    }
                                    //Decrement the current command line as it leaves the for cycle with one extra incrementation
                                    crntScriptLine--;
                                    lock (components.rteCmn)
                                    {
                                        components.rteCmn.crntCmdLine = crntScriptLine;
                                    }
                                    #endregion
                                }
                                else
                                    throw new Exception("Missing goto label statement!");
                            }
                        }

                        if (components.rteCmn.InvokeEnded)
                        {
                            lock (components.rteCmn)
                            {
                                components.rteCmn.InvokeEnded = false;
                                crntScriptLine = components.rteCmn.crntCmdLine;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        thrownErrors = ex.Message;
                        executionOK = false;
                    }
                }
                lock (components.rteCmn)
                {
                    components.rteCmn.crntCmdLine++;
                    crntScriptLine = components.rteCmn.crntCmdLine;
                }
            }

            #region Runtime error status string processing
            if (!executionOK)
            {
                errorStatus = "Failed executing " + components.rteCmn.c_source + " at command line: " + components.rteCmn.crntCmdLine.ToString() + "; processed to position: " + linePos.ToString() + ";";
                if (thrownErrors != null)
                {
                    errorStatus += "  Script raised error: " + thrownErrors + ";";
                }
                if (components.rteCmn.rteResult == null)
                    components.rteCmn.rteResult = unchecked((uint)-1);
            }
            else
            {
                errorStatus = "Successfuly executed: " + components.rteCmn.c_source + "::" + components.rteCmn.crntCmdLine.ToString() + " command lines.";
                if (components.rteCmn.rteResult == null)
                    components.rteCmn.rteResult = (uint)0;
            }
            #endregion

            scrExecDone();
        }

        /// <summary>
        /// Script source generation thread
        /// </summary>
        void sourcesifyScript()
        {
            if (execWatchDog++ == 100)
                throw new Exception("Too many script invokations - terminating current thread!");

            int linePos = 0;
            bool executionOK = true;
            string c_source = "";
            string thrownErrors = null;
            string moduleName = components.rteCmn.c_source;

            components.rteCmn.ScrRecId = execWatchDog;

            for (; components.rteCmn.crntCmdLine < components.rteCmn.cmdLns.Length && executionOK; components.rteCmn.crntCmdLine++)
            {
                if (!ScrCmdParser.ExecLineValidity(components.rteCmn.cmdLns[components.rteCmn.crntCmdLine]))
                    continue;
                try
                {
                    linePos = 0;

                    while (linePos < components.rteCmn.cmdLns[components.rteCmn.crntCmdLine].Length && executionOK)
                    {
                        if (NewCmdCommence())
                            break;
                        c_source += ScrCmdParser.SourcesifyCmdLine(components.rteCmn.cmdLns[components.rteCmn.crntCmdLine], ref linePos, components);
                    }
                }
                catch (Exception ex)
                {
                    thrownErrors = ex.Message;
                    executionOK = false;
                }
            }

            if (execWatchDog == 1)
            {
                string tmp = c_source;
                c_source = ScrCmdParser.SourcesifyFinalizaiton(ref tmp, moduleName, components);

                c_source += tmp;
                c_source += "}\n";
            }

            components.rteCmn.c_source = c_source;

            #region Runtime error status string processing
            if (!executionOK)
            {
                errorStatus = "Failed executing " + components.rteCmn.c_source + " at command line: " + components.rteCmn.crntCmdLine.ToString() + "; processed to position: " + linePos.ToString() + ";";
                if (thrownErrors != null)
                {
                    errorStatus += "  Script raised error: " + thrownErrors + ";";
                }
                if (components.rteCmn.rteResult == null)
                    components.rteCmn.rteResult = unchecked((uint)-1);
            }
            else
            {
                errorStatus = "Successfuly executed: " + components.rteCmn.c_source + "::" + components.rteCmn.crntCmdLine.ToString() + " command lines.";
                if (components.rteCmn.rteResult == null)
                    components.rteCmn.rteResult = (uint)0;
            }
            #endregion

            scrSourcesificationDone();
        }
        /// <summary>
        /// Message-header source generation function
        /// </summary>
        /// <returns>Definition and initialization of source messages</returns>
        public string generateMessageSource()
        {
            string c_source_MSGDef = "";
            string c_source_MSGInit = "void scrMod_InitMessages()\n{\n";

            for (int i = 0; i < components.scrMessages.Length; i++)
            {
                c_source_MSGDef += "systemMessage_t* " + components.scrMessages[i].msgDisplayName + ";\n";
                c_source_MSGInit += "\t" + components.scrMessages[i].msgDisplayName + " = (systemMessage_t*)TIMM_OSAL_MallocExtn(sizeof(systemMessage_t), TIMM_OSAL_TRUE, 32, 0, NULL);\n";
                c_source_MSGInit += "\t" + components.scrMessages[i].msgDisplayName + "->uIdChannel.nId = " + components.scrMessages[i].msgId.UID + ";\n";
                c_source_MSGInit += "\t" + components.scrMessages[i].msgDisplayName + "->nStatus = " + components.scrMessages[i].msgStatus.UID + ";\n";
                c_source_MSGInit += "\t" + components.scrMessages[i].msgDisplayName + "->nPayloadSize = 0;\n";
                c_source_MSGInit += "\t" + components.scrMessages[i].msgDisplayName + "->pPayload = NULL;\n";
                c_source_MSGInit += "\n";
            }

            c_source_MSGInit += "}\n";

            return c_source_MSGDef + "\n" + c_source_MSGInit;
        }
    }
}
